package show.me.the.money.config.reportSchema.define;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.springframework.dao.DataAccessException;

import show.me.the.money.config.reportSchema.ReportDefine;
import show.me.the.money.config.reportSchema.ReportDefineFactory;
import show.me.the.money.config.schema.SchemaItem;
import show.me.the.money.dictionary.Dictionaries;
import show.me.the.money.dictionary.Dictionary;
import show.me.the.money.dictionary.DictionaryItem;
import show.me.the.money.util.ConfigValueParser;
import show.me.the.money.util.context.Context;


public class ReportSchemaDynamic extends ReportSchema {
	private static Log logger = LogFactory.getLog(ReportSchemaDynamic.class);

	/**
	 * @param doc
	 * @param hash
	 *            {@list:List,@field:String}
	 */
	@SuppressWarnings({ "unchecked", "deprecation" })
	public void setDefineDoc(Document doc)  {
		Element root = doc.getRootElement();
		id = root.attributeValue("id", "");
		title = root.attributeValue("title", "");

		Element cnd = root.element("condition");
		if (cnd != null) {
			try {
				if(cnds == null){
					cnds = jsonMapper.readValue(cnd.getText(),List.class);
				}
				else{
					if(cnds.get(0).equals("and")){
						cnds.add(jsonMapper.readValue(cnd.getText(),List.class));
					}
					else{
						List o = new ArrayList();
						o.add("and");
						o.add(cnds);
						o.add(jsonMapper.readValue(cnd.getText(),List.class));
						cnds = o;
					}
				}
			}catch (Exception e) {
				logger.error("parse reportCondition failed:", e);
			}
		}
		List<Element> els = root.selectNodes("queryArgs/arg");
		int i = 0;
		String defaultValue = "";
		String field = root.element("field").attributeValue("id");
		String dic = root.element("field").attributeValue("dic");
		if (dic == null)
			dic = field;
		for (Element el : els) {
			SchemaItem si = new SchemaItem(el, i);
			if (si.getId().equals(field)) {
				defaultValue = ConfigValueParser.parse(si.getDefaultValue(),
						ctx);
			}
			queryArgs.add(si);
			i++;
		}
		String code = (String) ctx.get("q." + field);
		if (code == null)
			code = defaultValue;
		els = root.elements("define");
		int startRenderIndex = 0;
		Dictionary dictionary = Dictionaries.instance().getDic(dic);
		List dictionaryItem = dictionary.getSlice(code, 3,
				"", 0, 0);
		if(dictionaryItem.size()==0){
			dictionaryItem=new ArrayList();
			dictionaryItem.add(dictionary.getItem(code));
		}
		for (int j = 0; j < dictionaryItem.size(); j++) {
			DictionaryItem item = (DictionaryItem) dictionaryItem.get(j);
			if (item.getKey().length() == this.getNextLevel(code)) {
				List<Element> ele_headers=((Element) els.get(0))
				.selectNodes("headers/item");
				for(Element el : ele_headers){
					if(el.attributeValue("func")!=null){
						el.addAttribute("id", item.getKey());
						el.addAttribute("alias", item.getText());
						int renderIndex = Integer.parseInt(el
								.attributeValue("renderIndex")) + 1;
						el.addAttribute("renderIndex", String
								.valueOf(renderIndex));
					}else if(field !=null && el.attributeValue("id").equals(field)){
						el.addAttribute("expr", "substr("+field+",1,"+this.getNextLevel(code)+")");
					}
				}
				ReportDefine define = ReportDefineFactory.createInstance(
						(Element) els.get(0).createCopy(), startRenderIndex);
				define.setReportSchema(this);
				define.setQueryArgs(queryArgs);
				startRenderIndex += define.getHeaderCount();
				// define.addCondition(cnds);
				try {
					List dynamicCnd=jsonMapper.readValue(String.format(
							"['like',['$','a.%s'],['s','%s']]", field, item
							.getKey()
							+ "%"),List.class);
					cnds = dynamicCnd;
					define.addCondition(cnds);
				}catch (Exception e) {
					logger.error("parse reportCondition failed:", e);
				}
				defines.add(define);
			}
		}

		Element el = root.element("digger");
		if (el != null) {
			diggers = new HashMap<String, String>();
			els = el.elements();
			for (Element dig : els) {
				diggers.put("match", dig.attributeValue("match"));
				diggers.put(dig.attributeValue("match"), dig
						.attributeValue("target"));
			}
		}

		el = root.element("chart");
		if (el != null) {
			chartInfo = new HashMap<String, String>();
			chartInfo.put("template", el.attributeValue("template"));
			chartInfo.put("chartType", el.attributeValue("chartType"));
			chartInfo.put("limit", el.attributeValue("limit"));
		}

	}

	/**
	 * @param code
	 * @return
	 */
	private int getNextLevel(String code) {
		switch (code.length()) {
		case 4:
			return 6;
		case 6:
			return 9;
		case 9:
			return 11;
		case 11:
			return 11;
		default:
			return code.length();
		}
	}

	@SuppressWarnings("unchecked")
	public List<HashMap> run(Context ctx) throws DataAccessException {
		List<HashMap> data = null;
		if (defines.size() == 1) {
			data = defines.get(0).runSingleMode(ctx);
		} else {
			HashMap<String, HashMap> container = new LinkedHashMap<String, HashMap>();
			for (ReportDefine define : defines) {
				define.runMutiMode(container, ctx);
			}
			data = new ArrayList<HashMap>();
			data.addAll(container.values());
			Collections.sort(data, new Comparator() {
				public int compare(Object o1, Object o2) {
					Iterator<String> it = ((HashMap) o1).keySet().iterator();
					for (; it.hasNext();) {
						String key = (String) it.next();
						if (key.indexOf("_") > -1) {
							String str1 = (String) ((HashMap) o1).get(key
									.split("_")[0]);
							String str2 = (String) ((HashMap) o2).get(key
									.split("_")[0]);
							if (str1 == null || str2 == null)
								break;
							if (Long.parseLong(str1) >Long.parseLong(str2)) {
								return 1;
							}
						}
					}
					return 0;
				}
			});

			if (data.size() > 0) {
				List<String> fields = new ArrayList<String>();
				for (ReportDefine define : defines) {
					fields.add(define.getHeaders(false).get(0).getId());
				}

				HashMap sumitem = new HashMap();
				sumitem.putAll(data.get(0));
				for (int i = 1; i < data.size(); i++) {
					HashMap ditem = data.get(i);
					for (String key : fields) {
						double sum = Double.parseDouble(sumitem.get(key)
								.toString())
								+ Double.parseDouble(ditem.get(key).toString());
						ditem.put(key, sum);
						sumitem.put(key, sum);
					}
				}
			}
			ctx.put("totalCount", data.size());
			ctx.put("pageSize", data.size());
			ctx.put("pageNo", 1);
		}
		return data;
	}

}
